/* eslint-disable */
import { saveAs } from 'file-saver'
import XLSX from 'xlsx'

function datenum(v, date1904) {
  if (date1904) {
    v += 1462;
  }
  let epoch = Date.parse(v);
  return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
}

function sheet_from_array_of_arrays(data) {
  const ws = {};
  let range = {
    s: { c: 10000000, r: 10000000 },
    e: { c: 0, r: 0 }
  };

  for (let row = 0; row < data.length; ++row) {
    for (let col = 0; col < data[row].length; ++col) {
      range.s.r = Math.min(range.s.r, row);
      range.s.c = Math.min(range.s.c, col);
      range.e.r = Math.max(range.e.r, row);
      range.e.c = Math.max(range.e.c, col);

      const cell = { v: data[row][col] };
      if (cell.v == null) continue;

      const cell_ref = XLSX.utils.encode_cell({ c: col, r: row });

      cell.t = getType(cell.v);

      if (cell.v instanceof Date) {
        cell.v = datenum(cell.v);
        cell.z = XLSX.SSF._table[14];
      }

      ws[cell_ref] = cell;
    }
  }

  if (range.s.c < 10000000) {
    ws['!ref'] = XLSX.utils.encode_range(range);
  }

  return ws;
}

function getType(value) {
  if (typeof value === 'number') {
    return 'n';
  }
  if (typeof value === 'boolean') {
    return 'b';
  }
  if (value instanceof Date) {
    return 'n';
  }
  return 's';
}

function Workbook() {
  if (!(this instanceof Workbook)) return new Workbook();
  this.SheetNames = [];
  this.Sheets = {};
}

function s2ab(s) {
  let buf = new ArrayBuffer(s.length);
  let view = new Uint8Array(buf);
  for (let i = 0; i < s.length; ++i) {
    view[i] = s.charCodeAt(i) & 0xFF;
  }
  return buf;
}

export function export_json_to_excel({
                                       multiHeader = [],
                                       header,
                                       data,
                                       filename = 'excel-list',
                                       merges = [],
                                       autoWidth = true,
                                       bookType = 'xlsx',
                                     } = {}) {
  const preparedData = [header, ...data];
  multiHeader.forEach(headerRow => preparedData.unshift(headerRow));

  const ws_name = "SheetJS";
  const wb = new Workbook();
  const ws = sheet_from_array_of_arrays(preparedData);

  if (merges.length > 0) {
    ws['!merges'] = (ws['!merges'] || []).concat(
      merges.map(item => XLSX.utils.decode_range(item))
    );
  }

  if (autoWidth) {
    ws['!cols'] = calculateColumnWidths(preparedData);
  }

  /* add worksheet to workbook */
  wb.SheetNames.push(ws_name);
  wb.Sheets[ws_name] = ws;

  // Save as file
  const wbout = XLSX.write(wb, {
    bookType,
    bookSST: false,
    type: 'binary',
  });
  saveAs(new Blob([s2ab(wbout)], { type: "application/octet-stream" }), `${filename}.${bookType}`);
}

function calculateColumnWidths(data) {
  return data.reduce((acc, row) => {
    row.forEach((val, colIndex) => {
      const width = getColumnWidth(val);
      if (acc[colIndex] === undefined || acc[colIndex].wch < width) {
        acc[colIndex] = { wch: width };
      }
    });
    return acc;
  }, []);
}

function getColumnWidth(value) {
  if (value == null) return 10;
  const str = value.toString();
  return str.charCodeAt(0) > 255 ? str.length * 2 : str.length;
}
